package com.nnnu.wsnackshop.config;

import com.nnnu.wsnackshop.filter.AnyRolesAuthorizationFilter;
import com.nnnu.wsnackshop.filter.JwtFilter;
import jakarta.servlet.Filter;
import lombok.RequiredArgsConstructor;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.jetbrains.annotations.NotNull;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
@RequiredArgsConstructor
public class ShiroConfig {
    private final JwtRealm jwtRealm;
    private final RedisTemplate<String, Object> redisTemplate;

    @Bean
    public SecurityManager securityManager(RedisCacheManager cacheManager) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(jwtRealm);
        // 设置为使用 Redis 缓存
        manager.setCacheManager(cacheManager);
        // 禁用 Session 功能
        DefaultSubjectDAO subjectDAO = (DefaultSubjectDAO) manager.getSubjectDAO();
        DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
        evaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(evaluator);
        SecurityUtils.setSecurityManager(manager);
        return manager;
    }

    /**
     * Redis 低级连接配置，自动读取 spring.redis.* 配置
     */
    @Bean
    public RedisManager redisManager() {
        return new RedisManager();
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
        factory.setSecurityManager(securityManager);

        // 注册自定义 JWT 过滤器
        Map<String, Filter> filters = new LinkedHashMap<>();
        filters.put("jwt", jwtFilter());
        filters.put("anyRoles", anyRolesFilter());
        factory.setFilters(filters);

        // 设置 过滤链 - JwtFilter已经内置了匿名路径的判断，我们只需配置需要角色的部分
        Map<String, String> filterChainMap = getSimplifiedFilterChainMap();
        factory.setFilterChainDefinitionMap(filterChainMap);
        return factory;
    }

    private @NotNull Map<String, String> getSimplifiedFilterChainMap() {
        Map<String, String> filterChainMap = new LinkedHashMap<>();

        // 需要管理员权限的接口
        filterChainMap.put("/api/admin/**", "jwt, anyRoles[admin,superAdmin]");

        // 需要骑手权限的接口
        filterChainMap.put("/rider/**", "jwt, anyRoles[rider]");

        // 其他所有接口，交给JwtFilter处理
        filterChainMap.put("/**", "jwt");

        return filterChainMap;
    }

    @Bean
    public JwtFilter jwtFilter() {
        return new JwtFilter(redisTemplate);
    }

    @Bean
    public AnyRolesAuthorizationFilter anyRolesFilter() {
        return new AnyRolesAuthorizationFilter();
    }

    /**
     * 1. 用于扫描所有 Shiro 注解（RequiresRoles、RequiresPermissions）
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

    /**
     * 2. 将 SecurityManager 注入到 Advisor，开启注解拦截器
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * Shiro 的 CacheManager 使用 Redis 实现
     */
    @Bean
    public RedisCacheManager shiroCacheManager(RedisManager redisManager) {
        RedisCacheManager cacheManager = new RedisCacheManager();
        cacheManager.setRedisManager(redisManager);
        cacheManager.setKeyPrefix("shiro:cache:");
        return cacheManager;
    }
}
